
#include "ql.h"

size_t list_length( List * list )
{
    size_t      result = 0;

    if( list != 0 && list->car != 0 )
    {
        do
        {
            ++ result;
            list = list->cdr;
        }
        while( list != 0 );
    }

    return result;
}

List * list_clone( AllocContext * c, List * list )
{
    List *          result = alloc_list( c );

    if( 0 != list->car )
    {
        do
        {
            Obj *   elem;
            if( list->car->type == OBJ_LIST )
            {
                elem = (Obj *)list_clone( c, (List *)list->car );
            }
            else
            {
                elem = list->car;
            }

            list_add( c, result, elem );

            list = list->cdr;
        }
        while( 0 != list );
    }

    return result;
}

bool list_check_elements_type( List * list, int type )
{
    if( list->car != 0 )
    {
        do
        {
            if( list->car->type != type )
            {
                return false;
            }

            list = list->cdr;
        }
        while( list != 0 );
    }

    return true;
}

void list_append( List * result, List * list )
{
    if( list->car != 0 )
    {
        if( result->car != 0 )
        {
            // find last element
            for( ; result->cdr != 0; result = result->cdr )
            {}

            result->cdr = list;
        }
        else
        {
            // result is empty
            result->car = list->car;
            result->cdr = list->cdr;
        }
    }
}

Obj * list_set_at( List * list, size_t pos, Obj * elem )
{
    size_t      n = 0;
    List *      l = list;

    do
    {
        if( n == pos )
        {
            Obj * old_elem = list->car;
            list->car = elem;
            return old_elem;
        }

        ++ n;
        list = list->cdr;
    }
    while( list != 0 );

    return 0;
}

void list_push( AllocContext * c, List * list, Obj * elem )
{
    if( list->car != 0 )
    {
        List *  new_list = alloc_list( c );
        new_list->car = list->car;
        new_list->cdr = list->cdr;       
        list->cdr = new_list;
    }

    list->car = elem;
}

Obj * list_pop( AllocContext * c, List * list )
{
    Obj *       result = list->car;

    if( list->cdr != 0 )
    {
        List *  cdr = list->cdr;
        list->car = cdr->car;
        list->cdr = cdr->cdr;

        free_list( c, cdr );
    }
    else
    {
        list->car = 0;
    }

    return result;
}

void list_add( AllocContext * c, List * list, Obj * elem )
{
    if( list->car == 0 )
    {
        list->car = elem;
        return;
    }

    for( ;; list = list->cdr )
    {
        if( list->cdr == 0 )
        {
            List * new_list = alloc_list( c );
            new_list->car = elem;
            list->cdr = new_list;
            return;
        }
    }
}

Obj * list_at( List * list, size_t pos )
{
    size_t      n;
    List *      l = list;

    for( n = 0; l != 0; l = l->cdr, ++ n )
    {
        if( n == pos )
        {
            return l->car;
        }
    }

    return 0;
}

List * list_reverse( AllocContext * c, List * list )
{
    List *      result = alloc_list( c );

    if( list->car != 0 )
    {
        for( ; list != 0; list = list->cdr )
        {
            list_push( c, result, list->car );
        }
    }

    return result;
}
